'use strict'

import os from 'node:os'

import * as ansi from 'tui-lib/util/ansi'
import telc from 'tui-lib/util/telchars'

import {flattenGrouplike, parentSymbol, searchForItem} from './playlist-utils.js'
import processSmartPlaylist from './smart-playlist.js'
import Backend from './backend.js'

function untilEvent(object, event) {
  return new Promise(resolve => {
    object.once(event, resolve)
  })
}

const resetLine = '\r\x1b[0J'
const dim = ansi.resetAttributes() + ansi.setAttributes([ansi.A_DIM])
const write = data => process.stdout.write(data)

async function game() {
  const backend = new Backend()

  const result = await backend.setup()
  if (result.error) {
    console.error(result.error)
    process.exit(1)
  }

  const QP = await backend.addQueuePlayer()

  // TODO: nah
  QP.setVolume(60)

  process.stdin.setRawMode(true)
  process.stdin.on('data', async data => {
    if (data[0] === 0x03) {
      await QP.stopPlaying()
      process.exit(0)
    }
  })

  const sourcePath = process.argv[2] || os.homedir() + '/Music'
  let grouplike = {source: ['crawl-local', sourcePath]}
  grouplike = await processSmartPlaylist(grouplike)
  const allTracks = flattenGrouplike(grouplike).items

  const displayTrack = (track, shouldLimit) => {
    const parent = track[parentSymbol]
    const limit = (shouldLimit
      ? text => text.length > 30 ? '…' + text.slice(-30) : text
      : text => text)
    process.stdout.write(limit(track.name))
    if (parent.name) {
      process.stdout.write(' (From ' + limit(parent.name) + ')')
    }
  }

  while (allTracks.length) {
    const track = allTracks[Math.floor(Math.random() * allTracks.length)]
    QP.setPause(false)
    const promise = untilEvent(QP, 'playing')
    QP.play(track)
    await promise
    console.log('-- Listen! Then press space to pause and make a guess. --')

    let startTime = Date.now()
    let playTime = 0
    let gaveUp = false
    let giveUpNext = false

    let trackMatch = null
    let input = ''
    const displayInput = () => {
      write(resetLine)
      write(' >> ' + ansi.setAttributes([ansi.A_BRIGHT]))
      write(input)
      write(dim + ' :: ' + ansi.resetAttributes())
      if (trackMatch) {
        displayTrack(trackMatch, true)
      } else {
        write(ansi.setForeground(ansi.C_RED))
        write('(None)')
        write(ansi.resetAttributes())
      }
      write(`\r\x1b[${4 + input.length}C`)
    }

    const fmtTime = () => {
      let t = (playTime + Date.now() - startTime) / 1000
      t = Math.floor(t * 10) / 10
      if (t % 1 === 0) {
        t = t + '.0'
      }
      return t + 's'
    }

    const echoFn = () => {
      write(resetLine + fmtTime())
    }

    while (true) {
      let echo
      if (!QP.player.isPaused) {
        echo = setInterval(echoFn, 50)
      }
      const key = await untilEvent(process.stdin, 'data')
      clearInterval(echo)
      if (key[0] === 0x10 || (key[0] === 0x20 && !QP.player.isPaused)) {
        if (QP.player.isPaused) {
          startTime = Date.now()
          console.log(resetLine + dim + '<Unpaused.>')
          write(ansi.resetAttributes())
        } else {
          console.log(resetLine + dim + `<Paused @ ${fmtTime()}. Type the track's name below! ^P to resume.>`)
          playTime += Date.now() - startTime
          write(ansi.resetAttributes())
          echoFn()
          displayInput()
        }
        QP.togglePause()
      /*
      } else if (key[0] === 0x3f && (!key.length || !QP.player.isPaused)) {
        QP.setPause(false)
        gaveUp = true
        break
      */
      } else if (QP.player.isPaused) {
        if (telc.isBackspace(key)) {
          input = input.slice(0, -1)
          giveUpNext = false
        } else if (telc.isEnter(key)) {
          if (trackMatch) {
            write('\n')
            try {
              if (trackMatch === track) {
                write(ansi.setAttributes([ansi.A_BRIGHT, ansi.C_GREEN]))
                write('-- You got it! --\n')
                displayTrack(trackMatch, false)
                break
              } else {
                write(ansi.setAttributes([ansi.A_BRIGHT, ansi.C_RED]))
                write('-- Not this one! --\n')
                displayTrack(trackMatch, false)
              }
            } finally {
              write(ansi.resetAttributes())
              write('\n')
            }
          } else {
            if (giveUpNext) {
              QP.setPause(false)
              gaveUp = true
              break
            } else {
              write(resetLine + '(Press enter twice in a row to reveal the answer.)\n')
              giveUpNext = true
            }
          }
        } else {
          input += key.toString()
          giveUpNext = false
        }
        trackMatch = searchForItem({items: allTracks}, input)
        displayInput()
      }
    }

    process.stdout.write(resetLine)

    if (gaveUp) {
      console.log('-- You chose to reveal the track that played. --')
      displayTrack(track, false)
      console.log('')
    }

    console.log('-- Press space to continue! ^C to quit. --')
    while (true) {
      const key = await untilEvent(process.stdin, 'data')
      if (key[0] === 0x20) {
        break
      }
    }
    console.log('')
  }
}

game().catch(err => console.error(err))

